import Axios, {type AxiosInstance, type AxiosRequestConfig, type CustomParamsSerializer} from "axios";
import type {PureHttpError, PureHttpRequestConfig, PureHttpResponse, RequestMethods} from "./types.d";
import {stringify} from "qs";
import NProgress from "../progress";
import router from "@/router/index";
import {formatToken} from "@/utils/auth";
import {useUserStoreHook} from "@/store/modules/user";
import {ElMessage} from "element-plus";

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  baseURL: import.meta.env.VITE_API_HOST,
  // 请求超时时间
  timeout: 10000,
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest",
    isBody: true,
    isEnableLoading: true //控制是否需要加载
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: {
    serialize: stringify as unknown as CustomParamsSerializer
  }
};
let Loading: any = null; //保存加载框方法
let loadingCount = 0; //记录请求次数
class PureHttp {
  /** `token`过期后，暂存待执行的请求 */
  private static requests = [];
  /** 防止重复刷新`token` */
  private static isRefreshing = false;
  /** 初始化配置对象 */
  private static initConfig: PureHttpRequestConfig = {};
  /** 保存当前`Axios`实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

  constructor() {
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }

  /** 重连原始请求 */
  private static retryOriginalRequest(config: PureHttpRequestConfig) {
    return new Promise(resolve => {
      PureHttp.requests.push((token: string) => {
        config.headers["Authorization"] = formatToken(token);
        resolve(config);
      });
    });
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: PureHttpRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as PureHttpRequestConfig;

    // 单独处理自定义请求/响应回调
    return new Promise((resolve, reject) => {
      PureHttp.axiosInstance
        .request(config)
        .then((response: undefined) => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  /** 单独抽离的`post`工具函数 */
  public post<T, P>(
    url: string,
    params?: AxiosRequestConfig<P>,
    config?: PureHttpRequestConfig
  ): Promise<T> {
    return this.request<T>("post", url, params, config);
  }

  /** 单独抽离的`get`工具函数 */
  public get<T, P>(
    url: string,
    params?: AxiosRequestConfig<P>,
    config?: PureHttpRequestConfig
  ): Promise<T> {
    return this.request<T>("get", url, params, config);
  }

  /** 请求拦截 */
  private httpInterceptorsRequest(): void {
    PureHttp.axiosInstance.interceptors.request.use(
      async (config: PureHttpRequestConfig): Promise<any> => {
        // 开启进度条动画
        NProgress.start();
        const { isBody, isEnableLoading } = config.headers;
        if (isBody) {
          config.data = { body: config.data };
        }
        if (isEnableLoading) {
          //判断是否需要加载
          if (loadingCount == 0) {
            Loading = ElLoading.service({
              lock: true,
              text: "Loading",
              background: "rgba(255, 255, 255, 0.8)"
            });
          }
          loadingCount++;
        }
        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof config.beforeRequestCallback === "function") {
          config.beforeRequestCallback(config);
          return config;
        }
        if (PureHttp.initConfig.beforeRequestCallback) {
          PureHttp.initConfig.beforeRequestCallback(config);
          return config;
        }
        /** 请求白名单，放置一些不需要`token`的接口（通过设置请求白名单，防止`token`过期后再请求造成的死循环问题） */
        const whiteList = ["/refresh-token", "/spUser/logon"];
        return whiteList.some(url => config.url.endsWith(url))
          ? config
          : new Promise(resolve => {
              const { userInfo } = useUserStoreHook();
              if (userInfo?.token) {
                config.headers["Authorization"] = userInfo?.token;
              } else {
                router.push({ path: "/login" }).then(() => {
                  ElMessage.error("请登录");
                });
                return false;
              }
              resolve(config);
            });
      },
      error => {
        return Promise.reject(error);
      }
    );
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    const instance = PureHttp.axiosInstance;
    instance.interceptors.response.use(
      (response: PureHttpResponse) => {
        const $config = response.config;
        // 关闭进度条动画
        NProgress.done();
        const { isEnableLoading } = response.config.headers;
        if (isEnableLoading) {
          loadingCount--;
          loadingCount == 0 && Loading?.close();
        }
        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof $config.beforeResponseCallback === "function") {
          $config.beforeResponseCallback(response);
          return response.data;
        }
        if (PureHttp.initConfig.beforeResponseCallback) {
          PureHttp.initConfig.beforeResponseCallback(response);
          return response.data;
        }

        // if (response.data.code == -1) {
        //   useUserStoreHook()
        //     .logOut()
        //     .then(() => {
        //       ElMessage.error(response.data.message);
        //     });
        //   return Promise.reject(response.data);
        // } else
        if (response.data.code !== 200) {
          ElMessage.error(response.data.message);
          return Promise.reject(response.data);
        } else {
          return response.data;
        }
      },
      (error: PureHttpError) => {
        ElMessage.error("系统错误，请联系管理员");
        const $error = error;
        $error.isCancelRequest = Axios.isCancel($error);
        // 关闭进度条动画
        NProgress.done();
        const { isEnableLoading } = error.config.headers;
        if (isEnableLoading) {
          loadingCount--;
          loadingCount == 0 && Loading?.close();
        }
        // 所有的响应异常 区分来源为取消请求/非取消请求
        return Promise.reject($error);
      }
    );
  }
}

export const http = new PureHttp();
